#include <windows.h>                              // Header File For Windows
#include <stdlib.h>
#include "glut.h"
#include <math.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <glaux.h>

#define PI 3.14215

using namespace std;

HDC			hDC=NULL;                   // Private GDI Device Context
HGLRC       hRC=NULL;                   // Permanent Rendering Context
HWND        hWnd=NULL;                  // Holds Our Window Handle
HINSTANCE   hInstance;                  // Holds The Instance Of The Application
 
bool        keys[256];                  // Array Used For The Keyboard Routine
bool        active=TRUE;                    // Window Active Flag
bool        fullscreen=TRUE;                // Fullscreen Flag Set To TRUE By Default
 
GLfloat     rquad;                      // Angle For The Quad     ( NEW )
GLfloat     rcube1;
GLfloat     rcube2;
GLfloat     rcube3;
GLint		objModel;

char* textur ="brick01.bmp";

unsigned char pressedkey='k';
char* objekt;

// Storage For One Texture
GLuint      texture[1];  

//CAM WASD
float runUPDOWN = 0;
float runLEFTRIGHT = 0;
float runBACKIN = 15.0;


// Hold one object
typedef struct vertice{
	float v[3];		// vertices array
	float n[3];		// normals array
};
//Texturen laden
AUX_RGBImageRec *LoadBMP(char *Filename)
{
    FILE *File=NULL;
	if (!Filename)                          // Make Sure A Filename Was Given
	{
		return NULL;                        // If Not Return NULL
	}
	File=fopen(Filename,"r");               // Check To See If The File Exists

	if (File)                               // Does The File Exist?
	{
		fclose(File);                       // Close The Handle
		return auxDIBImageLoad(Filename);   // Load The Bitmap And Return A Pointer

	}
	return NULL;
}


//TExture Mapping

int LoadGLTextures()                                // Load Bitmaps And Convert To Textures
{
	int Status=FALSE;
	AUX_RGBImageRec *TextureImage[1];                   // Create Storage Space For The Texture
	memset(TextureImage,0,sizeof(void *)*1);


	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if (TextureImage[0]=LoadBMP(textur))
	{
			Status=TRUE;                            // Set The Status To TRUE
			glGenTextures(1, &texture[0]);                  // Create The Texture
 
			// Typical Texture Generation Using Data From The Bitmap
			glBindTexture(GL_TEXTURE_2D, texture[0]);
			// Generate The Texture
			glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
	}

	if (TextureImage[0])                            // If Texture Exists
	{
			if (TextureImage[0]->data)                   // If Texture Image Exists
			{
				free(TextureImage[0]->data);             // Free The Texture Image Memory
			}
 
			free(TextureImage[0]);                      // Free The Image Structure
	}
    return Status;                              // Return The Status
}



void loadFile( char* filename )
{
	ifstream file( filename );
	if (!file) exit( 1 );

	int a,b,c;
	string string1;
	string line;		// Beachte: keine Größenvorgabe
	vector<vertice> vertices;

	glBegin( GL_TRIANGLES );
	while ( getline( file, line) )
	{
		cout << line << endl;
		vertice v;
		textur ="brick01.bmp";

		if (sscanf(line.c_str(),"v %f %f %f",&(v.v[0]),&(v.v[1]),&(v.v[2])))
		{
			if((v.v[0]>=100)||(v.v[1] >= 100)||(v.v[2]>=100)||(v.v[0]<=-100)||(v.v[1]<= -100)||(v.v[2]<=-100)){
				return;
			}
			vertices.push_back( v );
			printf( "valid: v %f %f %f\n", v.v[0], v.v[1], v.v[2] );
		}

		/*if (sscanf(line.c_str(),"vn %f %f %f",&(v.v[0]),&(v.v[1]),&(v.v[2])))
		{
			if((v.v[0]>=100)||(v.v[1] >= 100)||(v.v[2]>=100)||(v.v[0]<=-100)||(v.v[1]<= -100)||(v.v[2]<=-100)){
				return;
			}
			vertices.push_back( v );
			printf( "valid: v %f %f %f\n", v.v[0], v.v[1], v.v[2] );
		}	
		*/
		if (sscanf( line.c_str(), "f %i %i %i", &a, &b, &c ) )
		{

			//if((a>=100)||(b >= 100)||(c >= 500)||(a<=-100)||(b<= -100)||(c<=-500)){
			//	return;
			//}
			printf(("%i %i %i"),a,b,c);
			a--; b--; c--;
			//Normalen berechnen
			
			//Normale für das Dreieck
			//x normalisieren
			v.n[0]=(((vertices[a].v[1]-vertices[b].v[1])*(vertices[b].v[2]-vertices[c].v[2]))-((vertices[a].v[2]-vertices[b].v[2])*(vertices[b].v[1]-vertices[c].v[1])));
			//y normalisieren
			v.n[1]=(((vertices[a].v[2]-vertices[b].v[2])*(vertices[b].v[0]-vertices[c].v[0]))-((vertices[a].v[0]-vertices[b].v[0])*(vertices[b].v[2]-vertices[c].v[2])));
			//z normalisieren
			v.n[2]=(((vertices[a].v[0]-vertices[b].v[0])*(vertices[b].v[1]-vertices[c].v[1]))-((vertices[a].v[1]-vertices[b].v[1])*(vertices[b].v[0]-vertices[c].v[0])));
			
			//Normale Normalisieren:
			float temp = (v.n[0]*v.n[0])+(v.n[1]*v.n[1])+(v.n[2]*v.n[2]);
			float temptemp;
			temptemp=sqrt(temp);
			if(temptemp == 0){
				temp = 1;
			}else{
				temp = temptemp;
			}
			printf( "Normalsvorwurzel: v %f %f %f\n", v.n[0], v.n[1], v.n[2] );
			
			temptemp = v.n[0];
			v.n[0]= temptemp/temp;
			temptemp = v.n[1];
			v.n[1]= temptemp/temp;
			temptemp = v.n[2];
			v.n[2]= temptemp/temp;

			
			glTranslatef(0, 0, 12);
			//FLAT SHADING Normalen zuweißen
			glNormal3f(v.n[0], v.n[1], v.n[2]);
			//GOURAUD SHADING Normalen zuweißen
			temp = (v.n[0]+v.n[1]+v.n[2])/3;
			printf( "Normals: v %f %f %f\n", temp, temp, temp );
			glNormal3f(temp, temp, temp);
			//ENDE
			glEnable(GL_TEXTURE_2D);	
			glBindTexture(GL_TEXTURE_2D, texture[0]);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f( vertices[a].v[0], vertices[a].v[1], vertices[a].v[2] );
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f( vertices[b].v[0], vertices[b].v[1], vertices[b].v[2] );
			glTexCoord2f(1.0f, 0.0f);
			glVertex3f( vertices[c].v[0], vertices[c].v[1], vertices[c].v[2] );
			


			printf( "valid: f %i %i %i\n", a,b,c );
			printf( "valid: v %f %f %f\n", vertices[a].v[0],vertices[a].v[1],vertices[a].v[2]);
			printf( "valid: v %f %f %f\n", vertices[b].v[0],vertices[b].v[1],vertices[b].v[2]);
			printf( "valid: v %f %f %f\n", vertices[c].v[0],vertices[c].v[1],vertices[c].v[2]);
		}
		
	}
	glEnd();
	return;
}

void initDisplayList(){
	objModel = glGenLists(2);
	glNewList( objModel, GL_COMPILE );
	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	
	glBegin( GL_TRIANGLES );
		loadFile( objekt);
	glEnd();
	
	glEndList();

}

void init(void) 
{


	glClearColor (0.0, 0.0, 0.0, 0.0);
	 objekt ="tetrahedron.obj";


	//Lichquellen definieren
	
	//Lichtquelle No. 1 ROT DIREKTES Licht, gerichtete Lichtquelle
	GLfloat ambient0[] = { 0.0, 0.0, 0.0, 1.0 };
	GLfloat diffuse0[] = { 1.0, 0.0, 0.0, 1.0 };
	GLfloat specular0[] = { 1.0, 0.0, 0.0, 1.0 };
	GLfloat position0[] = { 4.0, 0.0, 0.0, 0.0 };	//Direktes Licht

	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse0);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular0);
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
	
	//Lichtquelle No. 2 GRUEN LICHT, Punktichtquelle
	GLfloat ambient1[] = { 0.0, 0.0, 0.0, 1.0 };
	GLfloat diffuse1[] = { 0.0, 1.0, 0.0, 1.0 };
	GLfloat specular1[] = { 0.0, 1.0, 0.0, 1.0 };
	GLfloat position1[] = { -4.0, 0.0, -3.0, 1.0 };	

	glLightfv(GL_LIGHT1, GL_AMBIENT, ambient1);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1);
	glLightfv(GL_LIGHT1, GL_SPECULAR, specular1);
	glLightfv(GL_LIGHT1, GL_POSITION, position1);


	//Lichtquelle No.3 Blau SPOTLIGHT!
	float LightDir[3] = {0.0f, 0.0f, -1.0f};
	GLfloat ambient2[] = { 0.0, 0.0, 0.0, 1.0 };
	GLfloat diffuse2[] = { 0.0, 0.0, 1.0, 1.0 };
	GLfloat specular2[] = { 0.0, 0.0, 1.0, 1.0 };
	GLfloat position2[] = { 0.0, 5.0, -5.0, 1.0 };

	glLighti(GL_LIGHT2, GL_SPOT_CUTOFF, 30);
	glLightfv(GL_LIGHT2, GL_POSITION, position2);
	glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, LightDir);
	glLightfv(GL_LIGHT2, GL_AMBIENT, ambient2);
	glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2);
	glLightfv(GL_LIGHT2, GL_SPECULAR, specular2);

	//Material No.1
	GLfloat mat_emission[] = {0.0, 0.0, 0.0, 0.0};
	GLfloat mat_ambient[] = { 0, 0, 0.0, 1.0 };
	GLfloat mat_diffuse[] = { 1, 1, 1, 1.0 };
	GLfloat mat_specular[] = { 1, 1, 1, 1.0 };
	GLfloat shininess[] = { 20.0 };

	glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

	
	glShadeModel(GL_SMOOTH);
}


void RininslandSimonLichtquellen(void)
{ 

	if (!LoadGLTextures())                          // Jump To Texture Loading Routine ( NEW )
    {
        return;                           // If Texture Didn't Load Return FALSE ( NEW )
    }
	
	glEnable(GL_DEPTH_TEST);
   //Licht No.1 einschalten:
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_LIGHT2);
	glEnable(GL_COLOR_MATERIAL);
	
	//Licht Ende.
	


	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity ();             /* clear the matrix */
   
	// viewing transformation 
	glColor3f(0.2f,0.2f,0.2f);
    
	gluLookAt (runLEFTRIGHT, runUPDOWN, runBACKIN, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	glPushMatrix();
	glTranslatef(0, 0, 0);
	glCallList(objModel);			//Ausgabe OBJ-Datei
	glPopMatrix();

	if(pressedkey=='k'){
 		glRotatef(rquad,0.0f,1.0f,0.0f);
	   // Erde
	   glPushMatrix();
	   glutSolidSphere(3,60,60);
	   glPopMatrix();
   
	  // Mond1
	   glRotatef(rcube1,1.0f,1.0f,1.0f);
	   glPushMatrix();
	   glTranslatef(-5, 3, -4);
	   glutSolidSphere(1,20,20);
	   glPopMatrix();
	   // Mond2
	   glRotatef(rcube3,1.0f,-3.0f,-1.0f);
	   glPushMatrix();
  
	   glTranslatef(-2, -3, -5);
	   glutSolidSphere(1,20,20);
	   glPopMatrix();
	   // Mond3
	   glRotatef(rcube2,-2.0f,-3.0f,-1.0f);
	   glPushMatrix();
	   glTranslatef(-5, 3, -4);
	   glutSolidSphere(1,20,20);
	   glPopMatrix();
		 // Mond4
	   glRotatef(rcube2+rcube3,-3.0f,3.0f,-2.0f);
	   glPushMatrix();
	   glTranslatef(-4, 5, -2);
	   glutSolidSphere(1,20,20);
	   glPopMatrix();
  }
	//Schachbrett
	if(pressedkey=='y'){
		LoadGLTextures(); 
		textur ="schach.bmp";
		glEnable(GL_TEXTURE_2D);	
		glBindTexture(GL_TEXTURE_2D, texture[0]);
		glPushMatrix();
		glTranslatef(0, 0, 12);
		glBegin (GL_TRIANGLES);
			glTexCoord2f(0.0f, 0.0f);
			glNormal3f(-1.0f, -1.0f, 0.0f);
			glVertex3f(-1.0f, -1.0f,  0.0f);
			glTexCoord2f(1.0f, 0.0f);
			glNormal3f(1.0f, -1.0f,  0.0f);
			glVertex3f(1.0f, -1.0f,  0.0f);
			glTexCoord2f(1.0f, 1.0f); 
			glNormal3f(1.0f, 1.0f,  0.0f);
			glVertex3f( 1.0f,  1.0f, 0.0f);
		glEnd();
		glBegin (GL_TRIANGLES);
			glTexCoord2f(0.0f, 0.0f);
			glNormal3f(-1.0f, -1.0f, 0.0f);
			glVertex3f(-1.0f, -1.0f,  0.0f);
			glTexCoord2f(1.0f, 1.0f); 
			glNormal3f(1.0f, 1.0f,  0.0f);
			glVertex3f( 1.0f,  1.0f, 0.0f);
			glTexCoord2f(1.0f, 0.0f);
			glNormal3f(-1.0f, 1.0f,  0.0f);
			glVertex3f(-1.0f, 1.0f,  0.0f);
		glEnd();
		glPopMatrix();
	}
	//----------------------------

	rquad+=0.010f;					//Rotierungsgeschwindigkeit
    rcube1-=0.030f;
	rcube2+=0.050f;
	rcube3-=0.080f;
	// -----------------------------------------------------------------------------------------------------
   glutSwapBuffers();
}

void idle( void )
{
	glutPostRedisplay();
}

void reshape (int w, int h)
{

   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
   glMatrixMode (GL_MODELVIEW);
}



void keyboard(unsigned char key, int x, int y)
{
	pressedkey = key;
   switch (key) {
   /*case 'y':
		objekt = "cube.obj";
		initDisplayList();
		 glLoadIdentity (); 
		break;
		*/
   case 'x':
		objekt  ="untitled2.obj";
		initDisplayList();
		 glLoadIdentity (); 
		break;
   case 'c':
		objekt  ="untitled.obj";
		initDisplayList();
		 glLoadIdentity (); 
		break;
   case 'b':		//3D Baum
		objekt  ="3dBaum.obj";
		initDisplayList();
		break;
	//WASD- Camera Bewegen
	case 'w':
		runUPDOWN +=0.1;
		break;
	case 's':
		runUPDOWN -=0.1;
		break;
	case 'a':
		runLEFTRIGHT -= 0.1;
		break;
	case 'd':
		runLEFTRIGHT += 0.1;
		break;
	case 'q':
		runBACKIN-= 0.1;
		break;
	case 'e':
		runBACKIN+= 0.1;
		break;
    case 27:
         exit(0);
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH);

   glutInitWindowSize (500, 500); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   glutIdleFunc( idle );
   init ();
   //initDisplayList();
   glutDisplayFunc(RininslandSimonLichtquellen); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
} 
